package domain.dao.impl;

import java.sql.SQLException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import util.DomainUtils;
import domain.dao.interfaces.IDAOContactGroup;
import domain.entities.impl.Contact;
import domain.entities.impl.ContactGroup;
import domain.entities.impl.User;
import domain.entities.interfaces.IContact;
import domain.entities.interfaces.IContactGroup;
import domain.entities.interfaces.IUser;

public class DAOContactGroup extends HibernateDaoSupport implements IDAOContactGroup {

	private HibernateTemplate hibernateTemplate;



	public void init() {
		setHibernateTemplate(getSessionFactory());
	}

	public void setHibernateTemplate(SessionFactory sessionFactory){
		this.hibernateTemplate= new HibernateTemplate(sessionFactory);
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#addContactGroup(org.hibernate.Session, int, java.lang.String, java.lang.String[])
	 */
	public int addContactGroup(
			int userId, final String groupName, String[] contactsOfGroup) throws Exception {


		ApplicationContext context = util.SpringUtils.getApplicationContext();		

		int idContactGroup = -1;
		IUser user = (IUser) hibernateTemplate.get(User.class, userId);
		ContactGroup contactGroup = 
				(ContactGroup) context.getBean("beanContactGroup");


		//Create the contact group
		contactGroup.setUser(user);
		contactGroup.setGroupName(groupName);
		user.getContactGroups().add(contactGroup);

		
		//Save and merge into db (merge mean saveOrUpdate) 
		hibernateTemplate.save(contactGroup);
		hibernateTemplate.merge(user);

		
		//Add group's members
		idContactGroup = contactGroup.getGroupId();

		if(contactsOfGroup != null) {
			IDAOContactGroup daoContactGroup = (IDAOContactGroup) context.getBean("beanDAOContactGroup");
			for(String strContactId : contactsOfGroup) {
				daoContactGroup.addContactToGroup(
						idContactGroup, 
						Integer.parseInt(strContactId));
			}
		}

		

		return idContactGroup;
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#deleteContactGroup(org.hibernate.Session, int)
	 */
	public void deleteContactGroup(int id) throws Exception {


		// delete contact and save the DB state 
		IContactGroup contactGroup = (IContactGroup) hibernateTemplate.get(
				ContactGroup.class, id);
		Set<IContact> contacts = new HashSet<IContact>(contactGroup.getContacts());

		for(IContact c : contacts) {
			removeContactFromGroup(id, c.getId());
		}

		contactGroup.getUser().removeContactGroup(id);
		
		hibernateTemplate.delete(contactGroup);
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#updateContactGroup(org.hibernate.Session, int, java.lang.String)
	 */
	public void updateContactGroup(
			IContactGroup cg, int id, final String groupName) throws Exception {

		int oldVersion = cg.getVersion();
		int newVersion;
		
		cg.setGroupName(groupName);

		//update into db
		hibernateTemplate.merge(cg);
		
		newVersion = cg.getVersion();
		
		if(oldVersion != newVersion) {
			for(IContact c : cg.getContacts()) {
				c.setChildsVersion(c.getChildsVersion()+1);
				hibernateTemplate.merge(c);
			}
		}
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#addContactToGroup(org.hibernate.Session, int, int)
	 */
	public void addContactToGroup(
			int idContactGroup, int idContact) throws Exception{


		IContactGroup contactGroup = (IContactGroup) hibernateTemplate.get(
				ContactGroup.class, idContactGroup);
		IContact contact = (IContact) hibernateTemplate.get(Contact.class, idContact);


		//check not already here
		if(contactGroup.getContact(idContact) == null){
			contactGroup.getContacts().add(contact);
			contact.getBooks().add(contactGroup);
		}


		//update into db
		hibernateTemplate.merge(contact);
		hibernateTemplate.merge(contactGroup);
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#addContactsToGroup(org.hibernate.Session, int, java.lang.String[])
	 */
	public void addContactsToGroup(int idContactGroup, String[] contactsToAdd) throws Exception {

		ApplicationContext context = util.SpringUtils.getApplicationContext();
		IDAOContactGroup daoContactGroup = (IDAOContactGroup) context.getBean("beanDAOContactGroup");


		for(String strIdContat : contactsToAdd) {

			daoContactGroup.addContactToGroup(
					idContactGroup, Integer.parseInt(strIdContat));
		}
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#addContactToGroups(org.hibernate.Session, java.lang.String[], int)
	 */
	public void addContactToGroups(
			String[] contactGroups, int idContact) throws Exception {

		ApplicationContext context = util.SpringUtils.getApplicationContext();
		IDAOContactGroup daoContactGroup = (IDAOContactGroup) context.getBean("beanDAOContactGroup");


		for(String strCGId : contactGroups) {
			daoContactGroup.addContactToGroup(
					Integer.parseInt(strCGId), idContact);
		}
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#removeContactFromGroup(org.hibernate.Session, int, int)
	 */
	public void removeContactFromGroup(
			int idContactGroup, int idContact) throws Exception{

		IContactGroup contactGroup = (IContactGroup) hibernateTemplate.get(
				ContactGroup.class, idContactGroup);
		IContact toRemove = contactGroup.getContact(idContact);


		if(toRemove != null){
			contactGroup.getContacts().remove(toRemove);
			toRemove.removeContactGroup(idContactGroup);
			
			//update into db
			hibernateTemplate.merge(contactGroup);
			hibernateTemplate.merge(toRemove);
		}
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#removeContactsFromGroup(org.hibernate.Session, java.lang.String[], int)
	 */
	public void removeContactsFromGroup(
			Session currentHibernateSession, 
			String[] contactGroups, int idContact) throws Exception {

		ApplicationContext context = util.SpringUtils.getApplicationContext();
		IDAOContactGroup daoContactGroup = (IDAOContactGroup) context.getBean("beanDAOContactGroup");


		for(String strCGId : contactGroups) {
			daoContactGroup.removeContactFromGroup(
					Integer.parseInt(strCGId), idContact);
		}
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#removeContactsFromGroup(org.hibernate.Session, java.lang.String[], int)
	 */
	public void removeContactFromGroups(String[] contactGroups, int idContact)
			throws Exception {
		ApplicationContext context = util.SpringUtils.getApplicationContext();
		IDAOContactGroup daoContactGroup = (IDAOContactGroup) context.getBean("beanDAOContactGroup");


		for(String strCGId : contactGroups) {
			daoContactGroup.removeContactFromGroup(
					Integer.parseInt(strCGId), idContact);
		}	
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#removeContactsFromGroup(org.hibernate.Session, int, java.lang.String[])
	 */
	public void removeContactsFromGroup(
			int idContactGroup, String[] contactsToRemove) throws Exception {

		ApplicationContext context = util.SpringUtils.getApplicationContext();
		IDAOContactGroup daoContactGroup = (IDAOContactGroup) context.getBean("beanDAOContactGroup");


		for(String strIdContat : contactsToRemove) {

			daoContactGroup.removeContactFromGroup(
					idContactGroup, Integer.parseInt(strIdContat));
		}
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#getAll()
	 */
	public List<IContactGroup> getAll() {
		return (List<IContactGroup>) hibernateTemplate
				.execute(new HibernateCallback<List<IContactGroup>>() {

					public List<IContactGroup> doInHibernate(Session session)
							throws HibernateException, SQLException {


						//Business code
						return DomainUtils.castList(
								IContactGroup.class,session.createCriteria(
										ContactGroup.class).list());
					}
				});
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#getByUserId(int)
	 */
	public List<IContactGroup> getByUserId(final int userId) {

		return (List<IContactGroup>) hibernateTemplate
				.execute(new HibernateCallback<List<IContactGroup>>() {

					public List<IContactGroup> doInHibernate(Session session)
							throws HibernateException, SQLException {


						//Business code
						Query query = session.createQuery("from ContactGroup where ID_USER = :userId");
						query.setParameter("userId", userId);
						return query.list();
					}
				});
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#getById(int)
	 */
	public IContactGroup getById(final int id) {

		return (IContactGroup) hibernateTemplate
				.execute(new HibernateCallback<IContactGroup>() {

					public IContactGroup doInHibernate(Session session)
							throws HibernateException, SQLException {


						//Business code
						Query query = session.createQuery(
								"FROM ContactGroup cg"
										+ " WHERE cg.groupId = :id");
						query.setParameter("id", id);
						List result = query.list();

						return result.size() > 0 ? 
								(IContactGroup) query.list().get(0) : 
									null;
					}
				});
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#getByContactId(int, int)
	 */
	public List<IContactGroup> getByContactId(
			final int userId, final int contactId) {

		return (List<IContactGroup>) hibernateTemplate
				.execute(new HibernateCallback<List<IContactGroup>>() {

					public List<IContactGroup> doInHibernate(Session session)
							throws HibernateException, SQLException {


						//Business code
						Query query = session.createQuery(
								"SELECT cg "
										+ " FROM ContactGroup cg, Contact c"
										+ " WHERE ID_USER = :userId "
										+ "       AND c.id = :contactId "
										+ "       AND c IN elements(cg.contacts)");

						query.setParameter("userId", userId);
						query.setParameter("contactId", contactId);
						return query.list();
					}
				});
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#searchContactGroup(java.lang.String)
	 */
	public List<IContactGroup> searchContactGroup(final String partOfGrouName){

		return (List<IContactGroup>) hibernateTemplate
				.execute(new HibernateCallback<List<IContactGroup>>() {

					public List<IContactGroup> doInHibernate(Session session)
							throws HibernateException, SQLException {


						//Business code
						Query query = session.createQuery(
								"FROM ContactGroup WHERE groupName LIKE '%"+partOfGrouName+"%'");

						return query.list();
					}
				});
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#getContainedContact(int)
	 */
	public List<IContact> getContainedContact(final int idContactGroup) {

		return (List<IContact>) hibernateTemplate
				.execute(new HibernateCallback<List<IContact>>() {

					public List<IContact> doInHibernate(Session session)
							throws HibernateException, SQLException {


						//Business code
						Query query = session.createQuery(
								"SELECT c FROM Contact c, ContactGroup cg"
										+ " WHERE cg.groupId = :groupId AND c IN elements(cg.contacts)"
								);
						query.setParameter("groupId", idContactGroup);
						return query.list();
					}
				});
	}

	/* (non-Javadoc)
	 * @see domain.dao.impl.IDAOContactGroup#getNotContainedContact(int)
	 */
	public List<IContact> getNotContainedContact(final int idContactGroup) {

		return (List<IContact>) hibernateTemplate
				.execute(new HibernateCallback<List<IContact>>() {

					public List<IContact> doInHibernate(Session session)
							throws HibernateException, SQLException {


						//Business code
						Query query = session.createQuery(
								"SELECT c FROM Contact c, ContactGroup cg"
										+ " WHERE cg.groupId = :groupId AND c NOT IN elements(cg.contacts)"
								);
						query.setParameter("groupId", idContactGroup);
						return query.list();
					}
				});
	}

}